<template>
  <!-- onselectstart="return false;"表示不让选择网页文字内容 -->
  <div
    class="slide-verify"
    :style="{ width: canvasWidth + 'px' }"
    onselectstart="return false;"
  >
    <!-- 图片加载遮蔽罩 -->
    <div
      :class="{ 'img-loading': isLoading }"
      :style="{ height: canvasHeight + 'px' }"
      v-if="isLoading"
    />
    <!-- 认证成功后的文字提示 -->
    <div
      class="success-hint"
      :style="{ height: canvasHeight + 'px' }"
      v-if="verifySuccess"
    >
      {{ successHint }}
    </div>
    <!--刷新按钮-->
    <div class="refresh-icon" @click="refresh" />

    <!--后端生成-->

    <!--验证图片-->
    <img
      ref="canvas"
      class="slide-canvas"
      :width="canvasWidth"
      :height="canvasHeight"
    />
    <!--阻塞块-->
    <img ref="block" :class="['slide-block', { 'verify-fail': verifyFail }]" />
    <!-- 滑动条 -->
    <div
      class="slider"
      :class="{
        'verify-active': verifyActive,
        'verify-success': verifySuccess,
        'verify-fail': verifyFail,
      }"
    >
      <!--滑块-->
      <div class="slider-box" :style="{ width: sliderBoxWidth }">
        <!-- 按钮 -->
        <div
          class="slider-button"
          id="slider-button"
          :style="{ left: sliderButtonLeft }"
        >
          <!-- 按钮图标 -->
          <div class="slider-button-icon" />
        </div>
      </div>
      <!--滑动条提示文字-->
      <span class="slider-hint">{{ sliderHint }}</span>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import { getcaptcha } from "@/api/admin/user";

const canvasWidth = ref(320);
const canvasHeight = ref(155);
const isLoading = ref(true);
// 验证提示文字
const successHint = ref("验证成功");
// 滑动条提示文字
const sliderHint = ref("向右滑动");
// 校验进行状态
const verifyActive = ref(false);
// 校验成功状态
const verifySuccess = ref(false);
// 校验失败状态
const verifyFail = ref(false);

// 移动的X轴坐标
const originX = ref(0);
// 移动的Y轴做坐标
const originY = ref(0);
// 拖动距离数组
const dragDistanceList = ref([]);

// 滑块箱拖动宽度
const sliderBoxWidth = ref(0);
// 滑块按钮距离左侧起点位置
const sliderButtonLeft = ref(0);

// 鼠标按下状态
const isMouseDown = ref(false);
// 时间戳，计算滑动时长
const timestamp = ref(null);

// 随机字符串
const nonceStr = ref("");

const canvas = ref(null);
const block = ref(null);
onMounted(() => {
  bindEvents();

  getCaptcha();
});
/* 后台获取验证码*/
function getCaptcha() {
  //取后端默认值
  const data = {};
  getcaptcha(data)
    .then((response) => {
      const data = response.data;
      nonceStr.value = data.nonceStr;
      block.value.src = data.blockSrc;
      block.value.style.top = data.blockY + "px";
      canvas.value.src = data.canvasSrc;
    })
    .finally(() => {
      isLoading.value = false;
    });
}
/* 事件绑定*/
const bindEvents = () => {
  // 监听鼠标按下事件
  document
    .getElementById("slider-button")
    .addEventListener("mousedown", (event) => {
      startEvent(event.clientX, event.clientY);
    });
  // 监听鼠标移动事件
  document.addEventListener("mousemove", (event) => {
    moveEvent(event.clientX, event.clientY);
  });
  // 监听鼠标离开事件
  document.addEventListener("mouseup", (event) => {
    endEvent(event.clientX);
  });
  // 监听触摸开始事件
  document
    .getElementById("slider-button")
    .addEventListener("touchstart", (event) => {
      startEvent(event.changedTouches[0].pageX, event.changedTouches[0].pageY);
    });
  // 监听触摸滑动事件
  document.addEventListener("touchmove", (event) => {
    moveEvent(event.changedTouches[0].pageX, event.changedTouches[0].pageY);
  });
  // 监听触摸离开事件
  document.addEventListener("touchend", (event) => {
    endEvent(event.changedTouches[0].pageX);
  });
};
/* 校验图片是否存在*/
function checkImgSrc() {
  return !!canvas.value.src;
}
/* 滑动开始事件*/
function startEvent(originX1, originY1) {
  if (!checkImgSrc() || isLoading.value || verifySuccess.value) {
    return;
  }

  originX.value = originX1;
  originY.value = originY1;
  isMouseDown.value = true;
  timestamp.value = +new Date();
}
/* 滑动事件*/
function moveEvent(originX1, originY1) {
  if (!isMouseDown.value) {
    return false;
  }
  const moveX = originX1 - originX.value;
  const moveY = originY1 - originY.value;
  if (moveX < 0 || moveX + 40 >= canvasWidth.value) {
    return false;
  }
  sliderButtonLeft.value = moveX + "px";
  let blockLeft =
    ((canvasWidth.value - 40 - 20) / (canvasWidth.value - 40)) * moveX;
  block.value.style.left = blockLeft + "px";
  verifyActive.value = true;
  sliderBoxWidth.value = moveX + "px";
  dragDistanceList.value.push(moveY);
}
// 监听自定义事件
const emit = defineEmits(["success", "fail", "again"]);
/* 滑动结束事件*/
function endEvent(originX1) {
  if (!isMouseDown.value) {
    return false;
  }
  isMouseDown.value = false;
  if (originX1 === originX.value) {
    return false;
  }
  console.log("2222");
  // 开始校验
  isLoading.value = true;
  // 校验结束
  verifyActive.value = false;
  // 滑动时长
  const timestamp1 = +new Date() - timestamp.value;
  // 移动距离
  const moveLength = parseInt(block.value.style.left);
  // 限制操作时长10S，超出判断失败
  if (timestamp1 > 10000) {
    verifyFailEvent();
  }
  // 人为操作判定
  else if (!turingTest()) {
    verifyFail.value = true;
    console.log("again");
    emit("again");
  } else {
    console.log("success");
    verifySuccessEvent(timestamp1);
    emit("success", { nonceStr: nonceStr.value, value: moveLength });
  }
}
function sum(x, y) {
  return x + y;
}

function square(x) {
  return x * x;
}
/* 图灵测试*/
function turingTest() {
  const arr = dragDistanceList.value; // 拖动距离数组
  const average = arr.reduce(sum) / arr.length; // 平均值
  const deviations = arr.map((x) => x - average); // 偏离值
  const stdDev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); // 标准偏差
  return average !== stdDev; // 判断是否人为操作
}
/* 校验成功*/
function verifySuccessEvent(timestamp1) {
  console.log("校验成功");
  isLoading.value = false;
  verifySuccess.value = true;
  const elapsedTime = (timestamp1 / 1000).toFixed(1);
  if (elapsedTime < 1) {
    successHint.value = `仅仅${elapsedTime}S，你的速度快如闪电`;
  } else if (elapsedTime < 2) {
    successHint.value = `只用了${elapsedTime}S，这速度简直完美`;
  } else {
    successHint.value = `耗时${elapsedTime}S，争取下次再快一点`;
  }
  console.log("执行");
}
/* 校验失败*/
function verifyFailEvent(msg) {
  verifyFail.value = true;
  console.log("失败", msg);
  emit("fail", msg);
  refresh();
}
/* 刷新图片验证码*/
function refresh() {
  // 延迟class的删除，等待动画结束
  setTimeout(() => {
    verifyFail.value = false;
  }, 500);
  isLoading.value = true;
  verifyActive.value = false;
  verifySuccess.value = false;
  block.value.style.left = 0;
  sliderBoxWidth.value = 0;
  sliderButtonLeft.value = 0;
  getCaptcha();
}
// 子组件默认包含是私有,要暴露出去
defineExpose({
  refresh,
});
</script>

<style scoped>
*	{

touch-action:none;

touch-action:pan-y;

}

.slide-verify {
  position: relative;
}

/*图片加载样式*/
.img-loading {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  z-index: 999;
  animation: loading 1.5s infinite;
  background-image: url(../assets/images/loading.svg);
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 100px;
  background-color: #737c8e;
  border-radius: 5px;
}

@keyframes loading {
  0% {
    opacity: 0.7;
  }
  100% {
    opacity: 9;
  }
}

/*认证成功后的文字提示*/
.success-hint {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.8);
  color: #2cd000;
  font-size: large;
}

/*刷新按钮*/
.refresh-icon {
  position: absolute;
  right: 0;
  top: 0;
  width: 35px;
  height: 35px;
  cursor: pointer;
  background: url("../assets/images/light.png") 0 -432px;
  background-size: 35px 470px;
}

/*验证图片*/
.slide-canvas {
  border-radius: 5px;
}

/*阻塞块*/
.slide-block {
  position: absolute;
  left: 0;
  top: 0;
}

/*校验失败时的阻塞块样式*/
.slide-block.verify-fail {
  transition: left 0.5s linear;
}

/*滑动条*/
.slider {
  position: relative;
  text-align: center;
  width: 100%;
  height: 40px;
  line-height: 40px;
  margin-top: 15px;
  background: #f7f9fa;
  color: #45494c;
  border: 1px solid #e4e7eb;
  border-radius: 5px;
}

/*滑动盒子*/
.slider-box {
  position: absolute;
  left: 0;
  top: 0;
  height: 40px;
  border: 0 solid #1991fa;
  background: #d1e9fe;
  border-radius: 5px;
}

/*滑动按钮*/
.slider-button {
  position: absolute;
  top: 0;
  left: 0;
  width: 40px;
  height: 40px;
  background: #fff;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  transition: background 0.2s linear;
  border-radius: 5px;
}

/*鼠标悬浮时的按钮样式*/
.slider-button:hover {
  background: #1991fa;
}

/*鼠标悬浮时的按钮图标样式*/
.slider-button:hover .slider-button-icon {
  background-position: 0 -13px;
}

/*滑动按钮图标*/
.slider-button-icon {
  position: absolute;
  top: 15px;
  left: 13px;
  width: 15px;
  height: 13px;
  background: url("../assets/images/light.png") 0 -26px;
  background-size: 35px 470px;
}

/*校验时的按钮样式*/
.verify-active .slider-button {
  height: 38px;
  top: -1px;
  border: 1px solid #1991fa;
}

/*校验时的滑动箱样式*/
.verify-active .slider-box {
  height: 38px;
  border-width: 1px;
}

/*校验成功时的滑动箱样式*/
.verify-success .slider-box {
  height: 38px;
  border: 1px solid #52ccba;
  background-color: #d2f4ef;
}

/*校验成功时的按钮样式*/
.verify-success .slider-button {
  height: 38px;
  top: -1px;
  border: 1px solid #52ccba;
  background-color: #52ccba !important;
}

/*校验成功时的按钮图标样式*/
.verify-success .slider-button-icon {
  background-position: 0 0 !important;
}

/*校验失败时的滑动箱样式*/
.verify-fail .slider-box {
  height: 38px;
  border: 1px solid #f57a7a;
  background-color: #fce1e1;
  transition: width 0.5s linear;
}

/*校验失败时的按钮样式*/
.verify-fail .slider-button {
  height: 38px;
  top: -1px;
  border: 1px solid #f57a7a;
  background-color: #f57a7a !important;
  transition: left 0.5s linear;
}

/*校验失败时的按钮图标样式*/
.verify-fail .slider-button-icon {
  top: 14px;
  background-position: 0 -82px !important;
}

/*校验状态下的提示文字隐藏*/
.verify-active .slider-hint,
.verify-success .slider-hint,
.verify-fail .slider-hint {
  display: none;
}
</style>
